#pragma once
#include <assert.h>
#include <functional>
#include "packet.h"
#include "com.h"
#include "optional.h"

namespace KRPC {

enum RespondType {
	SUCCESSFUL = 0,		//成功
	TIMEOUT = 1,		//超时
	NOT_FOUND = 2,		//服务未找到
	NOT_MATCH = 3,		//参数不匹配
	UNPACK_FAIL = 4,	//解包失败
	NO_ACCESS = 5,		//权限不允许
	UNKNOWN = 6,		//未知的错误,例如回复的mode与请求mode不一致
	INVALID = 7,		//接收包无效,例如校验验证失败
	NETWORK_FAULT = 8,	//网络故障
};

static inline const char* GetErrorString(RespondType errorCode) {
	switch (errorCode) {
		case SUCCESSFUL:	return "successful";
		case TIMEOUT:		return "timeout";
		case NOT_FOUND:		return "service not found";
		case NOT_MATCH:		return "parameter not match";
		case UNPACK_FAIL:	return "unpack rpc packet failed";
		case NO_ACCESS:		return "no permission";
		case INVALID:		return "rpc packet invalid";
		case NETWORK_FAULT: return "network fault";
		default: break;
	}
	return "unknown error";
}

class FuncBase
{
public:
	FuncBase(COM &_com) : com(_com), id(1), errorCode(SUCCESSFUL) {}
	virtual ~FuncBase() {}

	bool available(int msec = 100)
	{
		return send("heartbeat", RPC_HEARTBEAT) && recv(RPC_HEARTBEAT, msec);
	}

	RespondType getError()
	{
		return errorCode;
	}

protected:
	COM &com;
	uint32_t id;
	RespondType errorCode;

	template<typename ... Args>
	bool send(const std::string &name, MsgMode mode, Args&&... args)
	{
		Packet packet(1024, sizeof(MsgHead));
		MsgHead *head = packet.getHead();
		std::memset(head, 0, sizeof(MsgHead));
		head->mode = mode;
		head->ver = com.isLocal() ? 0 : 1;	//非本地连接则默认使用有校验版本,本地连接则默认使用无校验版本
		head->id = ++id;
		std::memcpy(head->name, name.c_str(), name.size());
		head->name[name.size()] = '\0';
		packet.pack(std::tuple<Args...>(std::forward<Args>(args)...));
		return com.send(packet) ? true : (errorCode = NETWORK_FAULT, false);
	}

	Packet receive(int msec)
	{
		Packet packet;
		com.recv(packet, msec);
		return std::move(packet);
	}

	template<typename R>
	RespondType receive(R &ret, MsgMode mode, int msec)
	{
		RespondType respond = TIMEOUT;
		Packet packet;
		while (com.recv(packet, msec)) {
			if (!packet.valid()) {
				respond = INVALID;
				break;
			}
			const MsgHead *h = packet.getHead();
			if (h->mode == mode && h->id == id) {
				respond = packet.unpack(ret) ? SUCCESSFUL : UNPACK_FAIL;
				break;
			} else {
				if (h->id != id) {
					//消息唯一标识不匹配, 消息丢弃, 避免后续消息紊乱
					continue;
				} else {
					switch (h->mode) {
						case RPC_NOT_MATCH: respond = NOT_MATCH; break;
						case RPC_NOT_FOUND: respond = NOT_FOUND; break;
						case RPC_NO_ACCESS: respond = NO_ACCESS; break;
						default: respond = UNKNOWN;
					}
					break;
				}	
			}
		}
		errorCode = respond;
		return respond;
	}

	template<typename R>
	bool recv(R &ret, MsgMode mode, int msec)
	{
		return receive(ret, mode, msec) == SUCCESSFUL;
	}

	bool recv(MsgMode mode, int msec)	//针对返回类型是void
	{
		RespondType respond = TIMEOUT;
		Packet packet;
		while (com.recv(packet, msec)) {
			if (!packet.valid()) {
				respond = INVALID;
				break;
			}
			const MsgHead *h = packet.getHead();
			if (h->mode == mode && h->id == id) {
				respond = SUCCESSFUL;
				break;
			} else {
				if (h->id != id) {
					//消息唯一标识不匹配, 消息丢弃, 避免后续消息紊乱
					continue;
				} else {
					switch (h->mode) {
						case RPC_NOT_MATCH: respond = NOT_MATCH; break;
						case RPC_NOT_FOUND: respond = NOT_FOUND; break;
						case RPC_NO_ACCESS: respond = NO_ACCESS; break;
						default: respond = UNKNOWN;
					}
					break;
				}	
			}
		}
		errorCode = respond;
		return respond == SUCCESSFUL;
	}
};

template<typename R>
struct Result;

template<typename R>
class Result
{
public:
	Result(std::function<bool(R &, int)> func) : getResult(std::move(func)) {}
	Result(std::nullptr_t) : getResult(nullptr) {}

	bool get(R &ret, int timeoutMsec = 500) //默认超时500ms,设置为0会一直阻塞,使用UDP通信时最好设置超时
	{
		return getResult ? getResult(ret, timeoutMsec) : false;
	}

	Optional<R> get(int timeoutMsec = 500)	//默认超时500ms,设置为0会一直阻塞,使用UDP通信时最好设置超时
	{
		R ret;
		return (getResult && getResult(ret, timeoutMsec)) ? Optional<R>(std::move(ret)) : nullptr;
	}

private:
	std::function<bool(R &, int)> getResult;
};

template<>
class Result<void>
{
public:
	Result(std::function<bool(int)> func) : getResult(std::move(func)) {}
	Result(std::nullptr_t) : getResult(nullptr) {}

	bool get(int timeoutMsec = 500) //默认超时500ms,设置为0会一直阻塞,使用UDP通信时最好设置超时
	{
		return getResult ? getResult(timeoutMsec) : false;
	}

private:
	std::function<bool(int)> getResult;
};

using Heartbeat = FuncBase;

} //end of namespace KRPC
